import os.path


def python_docx_debug():
    """debug python_docx"""
    import os
    from docx import Document

    file_path = r'C:/Users/Mr-Lu/Desktop/djbh/module-report.docx'
    f = Document(file_path)  # docx obj
    tables = f.tables  # docx tables
    # print(len(tables))
    # -----------------------------------
    # value = tables[3].cell(1,1).paragraphs[0].runs[0].text  # 可以打印单元格第一个段落第一个样式行字符串
    # print(value)
    # -----------------------------------
    cell_p = tables[3].cell(1, 1).paragraphs
    for p in cell_p:
        for r in p.runs:
            print([r.text])  # 可以循环打印单元格内不同段落的不同样式行字符串
    # -------------------------------------
    # value = tables[3].cell(1,1).text  # 打印单元格文本
    # print([value])  # 列表字符串保留\r\t等转义的字符串


def use_test():
    # 方法一
    from win32com.client import Dispatch
    from win32clipboard import EmptyClipboard
    import time

    s_time = time.time()  # start time

    word_app = Dispatch('Word.Application')
    path = r'C:\Users\susam\Desktop\djbh\process_documents\2211111-01_孢子跃迁网_测评报告.docx'
    word_app.Visible = 1  # 不显示程序界面
    word_app.DisplayAlerts = 0  # 不警告
    word_app.Documents.Open(path)  # 打开原始文档
    doc = word_app.ActiveDocument

    para = doc.Paragraphs  # 段落的迭代器
    for p in para:
        if p.Range.Text == '单项测评结果记录\r':
            if p.OutlineLevel == 1:
                start = p.Range.End + 1
        elif p.Range.Text == '漏洞扫描结果记录\r':
            if p.OutlineLevel == 1:
                end = p.Range.Start - 1  # 注意 在复制选中内容时 光标的范围要-1

    doc.Range(start, end).Select()
    word_app.Selection.Copy()  # 复制选择的内容

    word_app.ActiveDocument.Close()  # 关闭文档

    doc_new = word_app.Documents.Open(r"D:\demo.docx")  # 打开新的文档
    # word_app.Documents(doc_new).Activate()  # 激活新的文档

    word_app.Selection.MoveRight(1, doc_new.Range().End - 1)  # 按字频移动 光标右边移动到文末前一个位置
    word_app.Selection.TypeParagraph()  # 插入回车
    word_app.Selection.InsertBreak()  # 插入页
    word_app.Selection.Paste()  # 粘贴选择的内容

    doc_new.SaveAs(r"D:\demo.docx")  # 保存新的文档
    doc_new.Close()  # 关闭文档

    e_time = time.time()  # end time
    print(f'[ replace ] {round((e_time - s_time) * 1000, 4)}ms')

    word_app.Quit(0)  # 关闭程序  参数: 0 不保存待定的更改


# TODO 设计更人性化的转换方法
def doc_to_docx():
    """将 .doc文件转换成 .docx文件"""
    import os
    from win32com import client

    wd = client.Dispatch('Word.Application')  # word程序对象
    path = r'C:\Users\Mr-Lu\Desktop\djbh\process_documents\template'
    for root, dirs, files in os.walk(path):
        _r = root.replace('/', '\\')
        for file in files:
            if file.endswith('.doc'):
                _f = os.path.join(root, file)
                doc = wd.Documents.Open(_f)
                doc.SaveAs(f'{_f}x', FileFormat=12)  # 另存为本地
                doc.Close()


def print_headers(fd):
    """打印文档的所有标题"""
    from docx.enum.style import WD_STYLE_TYPE

    heads = fd.styles
    for i in heads:
        if i.type == WD_STYLE_TYPE.PARAGRAPH:
            print(i.name)


# TODO 在web界面中补充缺失的一些信息 比如被测单位负责人...等
def get_doc_info(path) -> (dict, None):
    """
    获取报告的信息
    :param path 测评报告的文件路径 str
    """
    import os
    import re
    from docx import Document

    if len(path) == 0:
        return None
    report_name = os.path.basename(path).split('_')  # list
    if '测评报告' not in path:  # judge the file if the file is not a report with name
        print('[ file ] 选择的文档不是测评报告')
        return None
    data = {}
    # <editor-fold desc="从文档名称中获取项目编号">
    pj_num = report_name[0]
    if pj_num[0:7].isdigit():
        data['$项目编号$'] = pj_num
    else:
        data['$项目编号$'] = '/'
    f = Document(path)  # docx obj
    # </editor-fold>
    # TODO 使用遍历的方法避免扩展表引起的索引变动问题
    # <editor-fold desc="遍历文档表格获取信息">
    for table in f.tables:
        if table.cell(0, 0).text == '被测对象':
            # <editor-fold desc="网络安全等级测评基本信息表">
            # <editor-fold desc="被测对象">
            data['$被测对象$'] = table.cell(1, 1).text
            data['$安全保护等级$'] = table.cell(1, 3).text
            data['$备案证明编号$'] = table.cell(2, 1).text
            # </editor-fold>
            # <editor-fold desc="被测单位">
            table_0 = table.cell(3, 0).tables[0]  # 被测单位
            data['$被测单位$'] = table_0.cell(1, 1).text
            data['$被测单位地址$'] = table_0.cell(2, 1).text
            # TODO 关于合并单元格的索引说明
            # 表第1行有3列 表第2行有4列
            # 则表第1行非合并单元格应该对其第2行的对应进行索引
            # 合并单元格的列索引可以取步长中的任意值计算
            # 非合并单元格的列索引值应该参考最多列的行
            data['$被测单位邮编$'] = table_0.cell(2, 5).text
            data['$被测单位联系人姓名$'] = table_0.cell(3, 2).text
            data['$被测单位联系人职务$'] = table_0.cell(3, 4).text
            data['$被测单位联系人部门$'] = table_0.cell(4, 2).text
            data['$被测单位联系人办公电话$'] = table_0.cell(4, 4).text
            data['$被测单位联系人移动电话$'] = table_0.cell(5, 2).text
            data['$被测单位联系人电子邮件$'] = table_0.cell(5, 4).text
            # </editor-fold>
            # <editor-fold desc="测评单位">
            table_1 = table.cell(4, 0).tables[0]  # 测评单位
            data['$测评单位$'] = table_1.cell(1, 1).text
            data['$测评单位机构代码$'] = table_1.cell(1, 5).text
            data['$测评单位地址$'] = table_1.cell(2, 1).text
            data['$测评单位邮编$'] = table_1.cell(2, 5).text
            data['$测评单位联系人姓名$'] = table_1.cell(3, 2).text
            data['$测评单位联系人职务$'] = table_1.cell(3, 4).text
            data['$测评单位联系人部门$'] = table_1.cell(4, 2).text
            data['$测评单位联系人办公电话$'] = table_1.cell(4, 4).text
            data['$测评单位联系人移动电话$'] = table_1.cell(5, 2).text
            data['$测评单位联系人电子邮件$'] = table_1.cell(5, 4).text
            # </editor-fold>
            # </editor-fold>
        elif table.cell(0, 0).text == '测评结论和综合得分':
            # <editor-fold desc="等级测评结论">
            # <editor-fold desc="测评结论和综合得分">
            data['$被测对象描述$'] = table.cell(3, 1).text  # 表格中段落的字符串保留\r\n
            data['$安全状况描述$'] = table.cell(4, 1).text
            data['$等级测评结论$'] = table.cell(5, 1).text
            data['$综合得分$'] = table.cell(5, 3).text
            # </editor-fold>
            # </editor-fold>
        elif table.cell(0, 0).text == '等级测评结论扩展表（云计算安全）':
            data['$扩展要求$'] = '云计算'
        elif table.cell(0, 1).text == '设备名称':  # 漏扫结果汇总表
            if table.cell(0, 2).text == '系统及版本':
                if data.__contains__('$漏扫对象$'):  # JA/JB/JC...多个接入点的表
                    data['$漏扫对象$'] += [table.cell(r, 1).text for r in range(2, len(table.rows))]
                data['$漏扫对象$'] = [table.cell(r, 1).text for r in range(2, len(table.rows))]
        elif table.cell(0, 1).text == '文档名称':  # 文档/内容
            if data.__contains__('$制度文档$'):  # 过滤重复
                continue
            data['$制度文档$'] = [
                [table.cell(row, 1).text] + [table.cell(row, 2).text]
                for row in range(1, len(table.rows))]
        elif table.cell(0, len(table.columns) - 1).text == '测评项':
            ds = []
            index = 1
            for row_n in range(1, len(table.rows)):
                if table.rows[row_n].cells[0].text == table.rows[row_n].cells[1].text:
                    ds.append(table.rows[row_n].cells[0].text)
                    index = 1
                else:
                    ds.append([
                        table.rows[row_n].cells[4].text,
                        str(index),
                        table.rows[row_n].cells[5].text,
                        table.rows[row_n].cells[6].text,
                        table.rows[row_n].cells[1].text,
                        table.rows[row_n].cells[2].text,
                    ])
                    index += 1
            data['$问题建议$'] = ds
        elif table.cell(0, len(table.columns) - 1).text == '整改建议':
            for row_n in range(1, len(table.rows)):
                if table.rows[row_n].cells[0].text == table.rows[row_n].cells[1].text:
                    continue
                data['$问题建议$'][row_n - 1].append(table.rows[row_n].cells[5].text)
        else:
            # 数据回收处理
            if not data.__contains__('$扩展要求$'):
                data['$扩展要求$'] = '/'
    # </editor-fold>
    # print_headers(f)  # 打印所有标题
    # print(len(f.paragraphs))  # 段落的数量
    # <editor-fold desc="遍历文档段落获取信息">
    documents = f.paragraphs
    for i, doc in enumerate(documents):
        if len(doc.text) == 0:
            # print('回车空段落')
            continue
        if doc.style.name == 'Heading 2':
            if doc.text == '测评过程':  # 获取测评过程的时间参数
                reg = r'\d{4}年\d{2}月\d{2}日|\d{2}月\d{2}日'  # 正则方法
                keys_list = [
                    ['$测评准备起始时间$', '$测评准备结束时间$'],
                    ['$方案编制起始时间$', '$方案编制结束时间$'],
                    ['$现场实施起始时间$', '$现场实施结束时间$'],
                    ['$报告编制起始时间$', '$报告编制结束时间$']
                ]
                for num, keys in enumerate(keys_list, 2):
                    process_t = re.findall(reg, documents[i + num].text)
                    data[keys[0]] = process_t[0]
                    if '年' in process_t[1]:
                        data[keys[1]] = process_t[1]
                    else:
                        data[keys[1]] = f'{process_t[0][0:5]}{process_t[1]}'
                break  # 调试
        elif doc.style.name == 'Normal':
            if doc.text[0:4] == '报告编号':
                if data.__contains__('$报告编号$'):
                    continue
                data['$报告编号$'] = doc.text[5:]
    # </editor-fold>
    for k, v in data.items():
        print(f'{k} ---- *{v}*')
        # break  # 调试

    return data


def replace_doc_info(data, path) -> None:
    """
    替换word文件的内容
    :param data 键值数据 dict
        {'$key$': 'value'}
    :param path 测评报告的路径
    """
    import sys
    from win32com import client
    from docx import Document
    # 单元格对齐方式
    from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT
    # 段落对齐方式
    from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
    import time
    from config.doc_config import (
        template_file, template_map,
        main_path, project_path
    )

    if data is None:
        return None
    wd = client.Dispatch('Word.Application')  # word程序对象
    wd.Visible = True  # 显示word程序
    wd.DisplayAlerts = 0  # 不显示告警框

    s_time = time.time()  # start time

    # 通用方法
    for file in template_file:
        template_path = os.path.join(sys.path[0], f'{main_path}template/{file}')
        pj_path = os.path.join(sys.path[0], f'{project_path}{file}')
        if not template_map.__contains__(file):
            continue  # 临时过滤未处理的文件
        if not os.path.exists(template_path):
            print(f'[ file err ] 读取的文件 -- {file} -- 不存在')
            continue  # 过滤实际目录中未存在的文件
        # 执行 python-docx库
        if file == 'ZJXP QR26-08 现场测评授权书.docx':
            f = Document(template_path)
            for table in f.tables:
                if table.cell(0, 1).text == '设备名称（型号）/系统名称':
                    for num, scan in enumerate(data['$漏扫对象$']):
                        table.add_row()
                        table.cell(num + 1, 0).paragraphs[0].text = str(num + 1)
                        table.cell(num + 1, 1).paragraphs[0].text = scan
            f.save(pj_path)
            template_path = pj_path  # 保证后续替换在新的文件中执行
        elif file == 'ZJXP QR26-10 文档接收 归还清单.docx':
            f = Document(template_path)
            for table in f.tables:
                if table.cell(0, 1).text == '文件名称':
                    for num, rag in enumerate(data['$制度文档$']):
                        table.add_row()
                        table.cell(num + 1, 0).paragraphs[0].text = str(num + 1)
                        table.cell(num + 1, 1).paragraphs[0].text = rag[0]
                        table.cell(num + 1, 2).paragraphs[0].text = rag[1]
            f.save(pj_path)
            template_path = pj_path  # 保证后续替换在新的文件中执行
        elif file == 'ZJXP QR26-13 网络安全问题汇总及整改建议.docx':
            f = Document(template_path)
            for table in f.tables:
                if table.cell(0, 0).text == '安全层面':
                    for num, rag in enumerate(data['$问题建议$']):
                        table.add_row()
                        if isinstance(rag, str):
                            for mer in range(1, 7):
                                table.cell(num + 1, mer - 1).merge(table.cell(num + 1, mer))
                            table.cell(num + 1, 0).vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
                            table.cell(num + 1, 0).paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
                            table.cell(num + 1, 0).paragraphs[0].text = rag
                        else:
                            for n, col in enumerate(rag):
                                table.cell(num + 1, n).vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
                                table.cell(num + 1, n).paragraphs[0].text = rag[n]

            f.save(pj_path)
            template_path = pj_path  # 保证后续替换在新的文件中执行
        elif file == 'ZJXP QR26-09 测评结果记录表.docx':
            wd.Documents.Open(path)  # 打开原始文档
            doc = wd.ActiveDocument

            # 通过range判断选范围
            para = doc.Paragraphs  # 段落的迭代器
            for p in para:
                if p.Range.Text == '单项测评结果记录\r':
                    if p.OutlineLevel == 1:
                        start = p.Range.End + 1
                elif p.Range.Text == '漏洞扫描结果记录\r':
                    if p.OutlineLevel == 1:
                        end = p.Range.Start - 1  # 注意 在复制选中内容时 光标的范围要-1

            doc.Range(start, end).Select()
            wd.Selection.Copy()  # 复制选择的内容

            wd.ActiveDocument.Close()  # 关闭文档

            doc_new = wd.Documents.Open(template_path)  # 打开新的文档
            # word_app.Documents(doc_new).Activate()  # 激活新的文档

            wd.Selection.MoveRight(1, doc_new.Range().End - 1)  # 按字频移动 光标右边移动到文末前一个位置
            wd.Selection.TypeParagraph()  # 插入回车
            wd.Selection.InsertBreak()  # 插入页
            wd.Selection.Paste()  # 粘贴选择的内容

            doc_new.SaveAs(pj_path)  # 保存新的文档
            doc_new.Close()  # 关闭文档

            template_path = pj_path  # 保证后续替换在新的文件中执行

        # 执行 win32com库
        docx = wd.Documents.Open(template_path)  # document
        select = wd.Selection  # selection 需要在指定Open的文档代码后面设置 否则保存
        for rep in template_map[file]:
            if data.__contains__(rep):  # 过滤data中不存在的关键字
                select.Find.Execute(rep, False, False, False, False, False, True, 1, False, data[rep], 2)
        docx.SaveAs(pj_path)  # save as
        docx.Close()  # close

    # # 单个文件调试法
    # file = template_file[10]
    # # ---------------------------------------
    # template_path = f'{main_path}template\\{file}'
    # project_path = f'{project_path}{file}'
    # # 执行 python-docx库
    # if file == 'XXX.docx':
    #     pass

    # # 执行 win32com库
    # docx = wd.Documents.Open(project_path)  # document
    # select = wd.Selection  # selection 需要在指定Open的文档代码后面设置 否则保存
    # for rep in template_map[file]:
    #     if data.__contains__(rep):  # 过滤data中不存在的关键字
    #         select.Find.Execute(rep, False, False, False, False, False, True, 1, False, data[rep], 2)
    # docx.SaveAs(FileName=project_path)  # save as
    # docx.Close()  # close

    # TODO 多个文档处理完后统一关闭接口程序
    wd.Quit(0)  # 关闭程序  参数: 0 不保存待定的更改

    e_time = time.time()  # end time
    print(f'[ replace ] {round((e_time - s_time) * 1000, 4)}ms')


def choose_report():
    """选择测评报告文件"""
    import win32ui

    dlg = win32ui.CreateFileDialog(True)  # file-dialog
    dlg.SetOFNInitialDir(r'C:\Users\Mr-Lu\Desktop\djbh\process_documents')  # start path
    dlg.DoModal()  # implement
    file_name = dlg.GetPathName()  # wait for choosing . . .
    return file_name  # the value will be '' if close the window


if __name__ == '__main__':
    pass

    # python_docx_debug()

    # -------------------------------------

    # doc_to_docx()  # doc -> docx

    file_path = choose_report()  # file dialog

    report_data = get_doc_info(file_path)  # get report info

    # replace_doc_info(report_data, file_path)  # replace files content

    # ---------------------------------------------

    # use_test()
